package jvs.peer.xmpp;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import jvs.peer.sec.AccessControl;
import jvs.peer.sec.Group;
import jvs.peer.sec.User;

import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.RosterGroup;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.util.StringUtils;

/**
 * @author qiangli
 * 
 */
public class XmppAccessControl implements AccessControl {

	private XMPPConnection conn = null;

	public XmppAccessControl(XMPPConnection conn) {
		this.conn = conn;
	}

	public boolean addGroup(String group) {
		try {
			Roster r = conn.getRoster();
			r.createGroup(group);
			return true;
		} catch (Exception e) {
			new RuntimeException(e.getMessage());
		}
		return false;
	}

	public boolean checkGroup(String principal, String group) {
		Roster r = conn.getRoster();
		RosterGroup rg = r.getGroup(group);
		if (rg == null) {
			return false;
		}
		RosterEntry re = rg.getEntry(principal);
		return (re != null);
	}

	public boolean checkGroup(String group) {
		RosterGroup g = conn.getRoster().getGroup(group);
		return (g != null);
	}

	/**
	 * @return true if peer is in a group
	 */
	public boolean checkPrincipal(String principal) {
		RosterEntry re = conn.getRoster().getEntry(principal);
		if (re == null) {
			return false;
		}
		Collection<RosterGroup> g = re.getGroups();
		return (g != null && !g.isEmpty());
	}

	public boolean addPrincipal(String principal, String name, String group) {
		try {
			conn.getRoster().createEntry(principal, name,
					new String[] { group });
			return true;
		} catch (XMPPException e) {
			throw new RuntimeException(e.getMessage());
		}
	}

	public boolean removePrincipal(String uid, String gid) {
		Roster r = conn.getRoster();
		RosterGroup g = r.getGroup(gid);
		if (g == null) {
			throw new RuntimeException("No such group. " + gid);
		}
		RosterEntry e = g.getEntry(uid);
		if (e == null) {
			throw new RuntimeException("User not found in group. " + gid);
		}
		try {
			g.removeEntry(e);
			return true;
		} catch (XMPPException e1) {
			e1.printStackTrace();
		}
		return false;
	}

	public List<Group> getGroups(String uid) {
		Roster r = conn.getRoster();
		RosterEntry re = r.getEntry(uid);
		if (re == null) {
			throw new RuntimeException("No such user. " + uid);
		}
		List<Group> l = new ArrayList<Group>();
		for (RosterGroup rg : re.getGroups()) {
			l.add(new XmppPeerGroup(rg.getName(), conn));
		}
		return l;
	}

	public List<Group> groups() {
		Roster r = conn.getRoster();
		Collection<RosterGroup> gc = r.getGroups();

		List<Group> l = new ArrayList<Group>();
		for (RosterGroup rg : gc) {
			l.add(new XmppPeerGroup(rg.getName(), conn));
		}
		return l;
	}

	public List<User> getPrincipals(String group) {
		List<User> l = new ArrayList<User>();
		RosterGroup g = conn.getRoster().getGroup(group);
		if (g == null) {
			throw new RuntimeException("No such group. " + group);
		}
		for (RosterEntry e : g.getEntries()) {
			l.add(new XmppPeerPrincipal(e.getUser(), e.getName()));
		}
		return l;
	}

	public boolean removeGroup(String group) {
		Roster r = conn.getRoster();
		RosterGroup g = r.getGroup(group);
		if (g == null) {
			throw new RuntimeException("No such group. " + group);
		}
		// to remove group, remove all roster entries in the group
		for (RosterEntry e : g.getEntries()) {
			try {
				g.removeEntry(e);
			} catch (XMPPException x) {
				x.printStackTrace();
				return false;
			}
		}
		return true;
	}

	public String getJid() {
		String u = conn.getUser();
		return StringUtils.parseBareAddress(u);
	}

	public boolean updateGroup(String group, String newgroup) {
		Roster r = conn.getRoster();
		RosterGroup g = r.getGroup(group);
		if (g == null) {
			throw new RuntimeException("No such group. " + group);
		}
		RosterGroup ng = r.getGroup(newgroup);
		if (ng != null) {
			throw new RuntimeException("Group exists. " + newgroup);
		}
		g.setName(newgroup);
		return false;
	}

	public boolean updateUser(String uid, String name) {
		Roster r = conn.getRoster();
		RosterEntry e = r.getEntry(uid);
		if (e == null) {
			throw new RuntimeException("No such user. " + uid);
		}
		e.setName(name);
		return false;
	}

	public Group findGroup(String gid) throws Exception {
		RosterGroup g = conn.getRoster().getGroup(gid);
		return new XmppPeerGroup(g.getName(), conn);
	}

	public User findUser(String uid) throws Exception {
		RosterEntry e = conn.getRoster().getEntry(uid);
		return new User(e.getUser(), e.getName());
	}

	public User findUser(String gid, String uid) throws Exception {
		Roster r = conn.getRoster();
		RosterGroup g = r.getGroup(gid);
		RosterEntry e = g.getEntry(uid);
		return new User(e.getUser(), e.getName());
	}

	public List<User> users() {
		List<User> l = new ArrayList<User>();
		for (RosterEntry e : conn.getRoster().getEntries()) {
			l.add(new User(e.getUser(), e.getName()));
		}
		return l;
	}

	private static final String HOTWEB_GROUP = "hotweb";

	/**
	 * Add user to hotweb group if user is not in the group; create user if user
	 * entry does not exist.
	 */
	public boolean addUser(String user, String name) {
		Roster roster = conn.getRoster();
		try {
			RosterGroup rg = roster.getGroup(HOTWEB_GROUP);
			if (rg == null) {
				rg = roster.createGroup(HOTWEB_GROUP);
			}
			RosterEntry re = roster.getEntry(user);
			if (re == null) {
				roster.createEntry(user, name, new String[] { HOTWEB_GROUP });
				re = roster.getEntry(user);
			}
			if (!rg.contains(re)) {
				rg.addEntry(re);
			}
			return true;
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * Remove user from hotweb group
	 */
	public boolean removeUser(String user) {
		Roster roster = conn.getRoster();
		
		try {
			RosterEntry re = roster.getEntry(user);
			if (re == null) {
				return true;
			}
			RosterGroup rg = roster.getGroup(HOTWEB_GROUP);
			if (rg == null) {
				return true;
			}
			rg.removeEntry(re);
			
			return true;
		} catch (XMPPException e) {
			e.printStackTrace();
		}
		return false;
	}
}